/*
**  File: AquaPseudo.c
**
**  This file is part of the AQUA program suite, which is being
**  developed in the NMR Spectroscopy Department, Bijvoet Center for 
**  Biomolecular Research, Utrecht. 
**
**
**  AUTHOR(S) : Ton Rullmann
**  VERSION   : 1.5.0
**  DATE      : Dec 12, 1997
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "Compiler.h"
#include "General.h"

#include "AquaMacros.h"
#include "AquaTypes.h"

/****************  function prototypes  ****************/

#include "AquaFuncts.h"
#include "AquaFuncts_io.h"
#include "AquaStrucset.h"
#include "AquaFiles.h"
#include "MenuFuncts.h"
#include "Functs.h"

#include "AquaPseudo.h"

/****************  globals  ****************/

extern Boolean  batch;                               /* import from Functs */
extern Card     msg_string;                          /* import from Functs */
extern int      terseness;                   /* import from Functs */
char            *program_name = "AquaPseudo";        /* export to Functs */
char            *program_vers = aqua_version;        /* export to Functs */
char            *lib_nam;

enum { opt_pslib, opt_mr, opt_mrold, opt_libtyp };

/* BEGIN_EXTH */

/****************  main  ****************/
/*
**  Make pseudo atoms.
**
**  Special exit values:
**    10: pseudo atoms could not be made
*/

int main( int argc, char *argv[] )
{
    Strucset    theSet, psSet, fullSet, *setptr;
    Psatom      *psatoms;
    int         pseudo_count = 0;
    int         item, exit_flag = 0;
    Boolean     done = false;
    char      temp_value[4];
    
    enum { item_mol, item_merge, item_make, item_show, item_write };
    static MenuItem menulist[] =
    {
        { item_EXEC,  "exec",  none, on },
        { item_SET,   "set",   none, on },
        { item_mol,   "mol",   none, on },
        { item_merge, "merge", none, off }, 
        { item_make,  "make",  none, off }, 
        { item_show,  "show",  none, off }, 
        { item_write, "write", none, off }, 
        { item_QUIT,  "quit",  none, on } 
    };
    enum { libt_aqua, libt_biosym };
    static MenuItem menulist_libtyp[] =
    {
        { libt_aqua,   "aqua",   none, on }, 
        { libt_biosym, "biosym", none, on }
    };
    static MenuItem menulist_rstinp[] =
    {
        { item_NONE,   "none",      none, on }, 
        { item_aqua,   "aqua",      none, on }, 
        { item_disman, "disman",    none, on }, 
        { item_disgeo, "disgeo",    none, on }, 
        { item_diana,  "diana",     none, on }, 
        { item_xplor,  "xplor",     none, on }, 
        { item_xplori, "xplor-inv", none, on }, 
        { item_biosym, "biosym",    none, on }, 
        { item_whatif, "whatif",    none, on }, 
        { item_local,  "local",     none, on }, 
        { item_user,   "user",      none, on }, 
        { item_copy,   "copy",      none, on }
    };                         /* same as in AquaWhat, AquaHow and GetLIBName */
    static MenuItem optionlist[] =
    {
        { opt_batch,  "batch",     logical, on }, 
        { opt_terseness, "terseness",   integer, on }, 
        { opt_prtaq,  "print",     logical, on }, 
        { opt_prtres, "printres",  logical, on }, 
        { opt_prtlib, "printlib",  logical, on }, 
        { opt_crdout, "coordout",  oneword, on }, 
        { opt_rstinp, "restrinp",  oneword, on }, 
        { opt_pslib,  "pseudolib", string,  on }, 
        { opt_atmlib, "atomlib",   string,  on },
        { opt_mr,     "mr",        logical, off },  /* obsolete */
        { opt_mrold,  "mrold",     logical, off },  /* obsolete */
        { opt_libtyp, "libtyp",    oneword, on }, 
	{ opt_find2,  "find2",     logical, on }
    };

    static Menu menu =
    {
        "Main menu", "command", "Give command:", 
        sizeof(menulist)/sizeof(MenuItem)
    };
    static Menu menu_libtyp =
    {
        "Libtyp menu", "type", "Give library type:", 
        sizeof(menulist_libtyp)/sizeof(MenuItem)
    };
    static Menu menu_rstinp =
    {
        "Restrinp menu", "name", "Give restraint type:", 
        sizeof(menulist_rstinp)/sizeof(MenuItem)
    };
    static Menu options =
    {
        "Options menu", "option", "Give option:", 
        sizeof(optionlist)/sizeof(MenuItem)
    }; 


/* initialize */

    ProgramInit( argc, argv );
    ProjectInit();    
    SetMenu( &menu, menulist );
    SetOptions( &options, optionlist );
    SetSubMenu( &options, opt_libtyp, &menu_libtyp, menulist_libtyp );
    SetSubMenu( &options, opt_rstinp, &menu_rstinp, menulist_rstinp );
    ModOption( opt_libtyp, "aqua" );
    ModOption( opt_rstinp, "none" );
    ModOption( opt_crdout, "aqua" );
    sprintf( temp_value, "%d", terseness );
    ModOption( opt_terseness, temp_value );    
    
/* the main menu loop */

    while ( !done )
    {
        item = ReadMenuItem();
        switch ( item )
        {
            case item_EXEC:
		DoExec();
                break;
            case item_SET:
		ReadOption();
		batch = OptionTrue( opt_batch );
                if ( !Check_General_Options () ) {
	            printf( "Error in main: general options set are not okay\n" );
	            MError( );
                }
                terseness   = GetOptionIVal ( opt_terseness );
                break;
            case item_mol:
		StrucsetInit( &theSet, "theSet", true );
                if ( ReadAquaSet( &theSet, -1 ) )
                {
                    DisableMenuItem( item_mol );
                    EnableMenuItem( item_merge );
                    EnableMenuItem( item_make );
                    EnableMenuItem( item_show );
                    EnableMenuItem( item_write );
                }
                break;
	    case item_merge:
                StrucsetInit( &psSet, "pseudoSet", true );
                if ( !ReadAquaSet( &psSet, -1 ) )
		    break;
		StrucsetInit( &fullSet, "explodedSet", true );
		StrucsetJoin( &fullSet, &theSet, &psSet );
		fullSet.type = explo_str_data;
	        if ( IsEmptyStr( fullSet.ident ) )
		    DefaultStrucsetIdent( &fullSet );
                DisableMenuItem( item_merge );
                DisableMenuItem( item_make );
                break;
            case item_make:
		if ( OptionTrue( opt_mrold ) )
		    ModOption( opt_mr, "true" );
		if ( OptionTrue( opt_pslib ) )
		    ModOption( opt_rstinp, "user" );
                psatoms = ProcessPseudo( &pseudo_count, theSet.molecule );
		if ( !psatoms || pseudo_count == 0 )
		{
		    puts( " * No pseudo atom definitions!" );
		    break;
		}
                StrucsetInit( &psSet, "pseudoSet", true );
                if ( !MakePseudoSet( &psSet, pseudo_count, psatoms, &theSet ) )
		{
		    exit_flag = 10;
		    if ( batch )
			done = true;
		    break;
		}
		else
		    exit_flag = 0;
		StrucsetInit( &fullSet, "explodedSet", true );
		StrucsetJoin( &fullSet, &theSet, &psSet );
		fullSet.type = explo_str_data;
	        if ( IsEmptyStr( fullSet.ident ) )
		    DefaultStrucsetIdent( &fullSet );
                DisableMenuItem( item_make );
                DisableMenuItem( item_merge );
                break;
            case item_show:
                if ( ( setptr = WhichStrucset() ) )
                    WriteMenu( setptr, true );
                break;
            case item_write:
                if ( ( setptr = WhichStrucset() ) )
                    WriteMenu( setptr, false );
                break;
            case item_QUIT:
            case item_NULL:
                done = true;
                break;
            default:
                ProcessMenuError( menu_fatal );
                break;
        }
    }
    exit( exit_flag );
}

/****************  ProcessPseudo  ****************/
/*
**  Set up a list of Psatom's, describing presence and construction
**  of pseudo atoms.
**  (is analogue of ProcessAtomLIB, but handles other type of descr. file
**  as well)
*/

Psatom *ProcessPseudo( int *pseudo_count, Mol *molptr )
{
    char     *usrnam, *lib_typ, *fct_name = "ProcessPseudo";
    Psatom   *pslist;
    Filnam   lib_filnam;
    FILE     *lib_fp = NULL;
    Boolean  done = false;
    Menu     *oldmenu;
       
    if ( terseness < 6 ) {
        STATUS( "get the pseudo atom definitions" );
    }
    lib_typ = GetOptionCVal( opt_libtyp );

    if ( EQ( lib_typ, "aqua" ) )

/* standard Aqua library type */

    {
	lib_nam = GetLIBName( opt_rstinp );        
	if ( !lib_nam )
	    return( NULL );			/* no processing done */

/* type "user": user defined library in Aqua format */

	if ( EQ( lib_nam, "user" ) )
	{
	    oldmenu = SetOneMenu( "Give name of pseudo-library:" );
	    usrnam = GetOptionCVal( opt_pslib );   /* use value of pseudolib option */
						   /* OK if option menu has not been swapped */
	    while ( !done )
	    {
		if ( !usrnam )
		    MReadWord( lib_filnam );
		else
		{
		    strcpy( lib_filnam, usrnam );
		    usrnam = NULL;                  /* to prevent infinite loop */
		}
		if ( terseness < 6 ) {
                    printf( "Now opening: %s\n", lib_filnam );
                }
		if ( ! ( done = OPENr( lib_fp, lib_filnam ) ) )
		{
		    printf( " * pseudo-library file <%s> could not be opened\n", lib_filnam );
		    MError();
		}
	    }
	    ResetMenu( oldmenu );
	    if ( terseness < 6 ) {
	        STATUS( "reading user PseudoLIB" );
            }
	}
	else

/* type "local", "diana", etc.: standard Aqua library from Aqua data directory */

	{
	    strcpy( lib_filnam, "PseudoLIB-" );
	    if ( OptionTrue( opt_mr ) )
		strcat( lib_filnam, lib_nam );    /* restraint file type -> lib name */
	    else
		strcat( lib_filnam, "aqua" );     /* standard PseudoLIB-aqua */
	    if ( OptionTrue( opt_mrold ) )
		strcat( lib_filnam, "-mr" );
	    lib_fp = AquaDataFile( lib_filnam );
	    if ( !lib_fp )
		exit( 1 );
	    if ( terseness < 6 ) {
	        STATUS( "reading standard PseudoLIB" );
            }
	}
	pslist = ReadPseudoLIB( lib_fp, pseudo_count );
    }

    else if ( EQ( lib_typ, "biosym" ) )

/* Biosym mdf file library type */

    {
	oldmenu = SetOneMenu( "Give name of mdf-file:" );
	while ( !done )
	{
	    MReadWord( lib_filnam );
	    if ( ! ( done = OPENr( lib_fp, lib_filnam ) ) )
	    {
		printf( " * mdf file <%s> could not be opened\n", lib_filnam );
		MError();
	    }
	}
	ResetMenu( oldmenu );
        if ( terseness < 6 ) {
	    STATUS( "reading mdf file" );
        }
	pslist = ReadMDFPseudo_biosym( lib_fp, pseudo_count );
    }    

    else
    {
	printf( " * Illegal library type %s\n", lib_typ );
	return( NULL );
    }

/* reading of library completed */

    if ( !pslist || *pseudo_count == 0 )
    {
	puts( " * Pseudo atom definition list empty" );
	return( NULL );
    }

/*
    sprintf( msg_string, "%i pseudo atom definitions read", *pseudo_count );
    STATUSm;
    PrintPsatomS( pslist, *pseudo_count );
*/


/* expand the list from 'wildcard' residue numbers to individual residues */

    if ( terseness < 6 ) {
        STATUS( "generating definitions for present molecule" );
    }
    pslist = ExpandPsatoms( pslist, pseudo_count, molptr );
    sprintf( msg_string, "%i pseudo atom definitions active for present molecule", 
             *pseudo_count );
    if ( terseness < 6 ) {
        STATUSm;
    }

    if ( OptionTrue( opt_prtlib ) )
        PrintPsatomS( pslist, *pseudo_count );

    return( pslist );
}

/****************  MakePseudoSet  ****************/
/*
**  Create a strucset of pseudo atoms using the Psatom list.
**
**  NOTE: the atoms are created by FillAtom, see there.
*/

Boolean MakePseudoSet( Strucset *psSet, const int pseudo_count, Psatom *psatoms, 
                       Strucset *theSet )
{
    char     *fct_name = "MakePseudoSet";
    char     *lib_nam_temp;
    Atom     at;
    Coord    sum;
    Struc    *strucptr, *psstrucptr;
    Mol      *molptr = theSet->molecule;
    size_t   len;
    int      i, idx, skip_count = 0, count = 0;
    int      whichnam_constit, whichnam_pseu;
    
    if ( terseness < 6 ) {
        STATUS( "create the pseudo atoms" );
    }
    if ( EMPTYSET( *psSet ) )
    {
        psSet->type = pseu_str_data;
        StrucsetCreate( psSet, pseudo_count, theSet->struc_count );
    }
    else
        psSet->type = explo_str_data;

    if ( OptionTrue( opt_mr ) )
    {
	if ( OptionTrue( opt_mrold ) )
	{
	    whichnam_constit = use_ext;
	    whichnam_pseu    = use_int;
	}
	else
	{
	    whichnam_constit = use_int;
	    whichnam_pseu    = use_ext;
	}
    }
    else
    {
	whichnam_constit = use_int;
	whichnam_pseu    = use_int;
    }

/* loop over the pseudo atoms */

    while ( count++ < pseudo_count )
    {
/* check whether all constituent atoms can be found */
	i = 0;
	while ( i < psatoms->num_form )
	{
	    at = FillAtom( psatoms->res_id, psatoms->res_nam, 
	                   psatoms->form_nam[i], whichnam_constit );
	    if ( !FindAtom2( &idx, &at, molptr, whichnam_constit, false ) )
	    {
		WriteNotFoundAtom( stdout, &at, whichnam_constit, whichnam_constit );

		at = FillAtom( psatoms->res_id, psatoms->res_nam,
		               psatoms->pseudo_nam, whichnam_pseu );
		printf( "Cannot make pseudo atom " );
		WriteAtom( stdout, &at, whichnam_pseu );
		putchar( '\n' );
		skip_count++;
		goto next;
	    }
	    i++;
	}
/* add the pseudo atom to the set */
        at = FillAtom( psatoms->res_id, psatoms->res_nam,
                       psatoms->pseudo_nam, whichnam_pseu );
        AddAtomToSet( psSet, &at );
/* loop over the structures */
        strucptr = theSet->fst_struc;
        psstrucptr = psSet->fst_struc;
        while ( strucptr )
        {
/* average the coordinates of the constituent atoms */
            CoordZero( &sum );
            i = 0;
            while ( i < psatoms->num_form )
            {
/* THIS COULD BE DONE MORE EFFICIENTLY: store the numbers..... */
                at = FillAtom( psatoms->res_id, psatoms->res_nam,
                               psatoms->form_nam[i], whichnam_constit );
                if ( !FindAtom2( &idx, &at, molptr, whichnam_constit, false ) )
                {
                    WriteNotFoundAtom( stdout, &at, whichnam_constit, whichnam_constit );
                    exit( 1 );
                }
                CoordAdd( &sum, strucptr->coords[idx] );
                i++;
            }
            CoordDiv( &sum, psatoms->num_form );
            AddCoordToSet( psSet, psstrucptr, &sum );
            psstrucptr = psstrucptr->next_struc;
            strucptr = strucptr->next_struc;
        }
next:
        psatoms++;
    }

/* integrity check */

    if ( skip_count )
    {
        if ( skip_count == pseudo_count )
    	    printf( " * No pseudo atoms could be made\n" );
        else
	{
	    printf( " * %i pseudo atoms could not be made\n", skip_count );
	    printf( " * %i pseudo atoms have been made\n", pseudo_count - skip_count );
	}
	psSet->atom_count_mem = psSet->atom_count;
	psSet->atom_count     -= skip_count;
    }
    
    if ( skip_count == pseudo_count )
	return( false );
    else
    {
	if ( !StrucsetOK( psSet, complete_set ) )
	{
	    printf( " * Pseudo atom strucset corrupt\n" );
	    exit( 1 );
	}
    }
    
    if ( IsEmptyStr( psSet->ident ) )
        DefaultStrucsetIdent( psSet );
	
    strcpy( psSet->derived, "Pseudo atoms derived from: " );
    len = strlen( psSet->derived ) + strlen( molptr->inp_filnam );
    len = ( len < size_LINE-1 ? len : size_LINE-1 );
    strncat( psSet->derived, molptr->inp_filnam, len - strlen( psSet->derived ) );

    MakeResidues( psSet, true );
    if ( OptionTrue( opt_atmlib ) ) {
        lib_nam_temp = "copy";
    } else {
        lib_nam_temp = lib_nam;
    }
    MapAtomsInSet( psSet, whichnam_pseu==use_ext? ext_to_int : int_to_ext, 
                   lib_nam_temp );
    
    return( true );
}

